//
//  NSArray+BLEAdd.m
//  BLESDK
//
//  Created by 李阳 on 2018/5/24.
//

#import "NSArray+BLEAdd.h"
#import <objc/objc.h>
#import <objc/runtime.h>

@interface NSArray_BLEAdd : NSObject
@end
@implementation NSArray_BLEAdd
@end



@implementation NSArray (BLEAdd)

- (NSArray *)ble_map:(id (^)(id obj, NSUInteger idx))block
{
    NSParameterAssert(block != nil);
    
    if (!self.count)
    {
        return nil;
    }
    
    NSMutableArray *result = [NSMutableArray arrayWithCapacity:self.count];
    
    [self enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
        id value = block(obj, idx) ?: [NSNull null];
        [result addObject:value];
    }];
    
    return result;
}

- (id)ble_match:(BOOL (^)(id obj))block
{
    NSParameterAssert(block != nil);
    
    NSUInteger index = [self indexOfObjectPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) {
        return block(obj);
    }];
    
    if (index == NSNotFound)
        return nil;
    
    return self[index];
}


@end


static const int block_key;

@interface _BLENSObjectKVOBlockTarget : NSObject

@property (nonatomic, copy) void (^block)(__weak id obj, id oldVal, id newVal);

- (id)initWithBlock:(void (^)(__weak id obj, id oldVal, id newVal))block;

@end

@implementation _BLENSObjectKVOBlockTarget

- (id)initWithBlock:(void (^)(__weak id obj, id oldVal, id newVal))block
{
    self = [super init];
    if (!self) return nil;
    self.block = block;
    return self;
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    if (!self.block) return;
    
    BOOL isPrior = [[change objectForKey:NSKeyValueChangeNotificationIsPriorKey] boolValue];
    if (isPrior) return;
    
    NSKeyValueChange changeKind = [[change objectForKey:NSKeyValueChangeKindKey] integerValue];
    if (changeKind != NSKeyValueChangeSetting) return;
    
    id oldVal = [change objectForKey:NSKeyValueChangeOldKey];
    if (oldVal == [NSNull null]) oldVal = nil;
    
    id newVal = [change objectForKey:NSKeyValueChangeNewKey];
    if (newVal == [NSNull null]) newVal = nil;
    
    self.block(object, oldVal, newVal);
}

@end


@implementation NSObject (BLEAdd)

- (void)ble_addObserverBlockForKeyPath:(NSString *)keyPath block:(void (^)(__weak id obj, id oldVal, id newVal))block
{
    if (!keyPath.length || !block) return;
    _BLENSObjectKVOBlockTarget *target = [[_BLENSObjectKVOBlockTarget alloc] initWithBlock:block];
    NSMutableDictionary *dic = [self _BLE_allNSObjectObserverBlocks];
    NSMutableArray *arr = dic[keyPath];
    if (!arr)
    {
        arr = [NSMutableArray new];
        dic[keyPath] = arr;
    }
    [arr addObject:target];
    [self addObserver:target forKeyPath:keyPath options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:NULL];
}

- (void)ble_removeObserverBlocksForKeyPath:(NSString *)keyPath
{
    if (!keyPath.length) return;
    NSMutableDictionary *dic = [self _BLE_allNSObjectObserverBlocks];
    NSMutableArray *arr = dic[keyPath];
    [arr enumerateObjectsUsingBlock: ^(id obj, NSUInteger idx, BOOL *stop) {
        [self removeObserver:obj forKeyPath:keyPath];
    }];
    
    [dic removeObjectForKey:keyPath];
}

- (void)ble_removeObserverBlocks
{
    NSMutableDictionary *dic = [self _BLE_allNSObjectObserverBlocks];
    [dic enumerateKeysAndObjectsUsingBlock: ^(NSString *key, NSArray *arr, BOOL *stop) {
        [arr enumerateObjectsUsingBlock: ^(id obj, NSUInteger idx, BOOL *stop) {
            [self removeObserver:obj forKeyPath:key];
        }];
    }];
    
    [dic removeAllObjects];
}

- (NSMutableDictionary *)_BLE_allNSObjectObserverBlocks
{
    NSMutableDictionary *targets = objc_getAssociatedObject(self, &block_key);
    if (!targets)
    {
        targets = [NSMutableDictionary new];
        objc_setAssociatedObject(self, &block_key, targets, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
    return targets;
}

@end


@implementation NSDateFormatter (BLEAdd)

+ (NSDateFormatter *)bleFormatter
{
    static NSDateFormatter *fmt;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        fmt = [[NSDateFormatter alloc]init];
        fmt.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
        fmt.dateFormat = @"HH:mm:ss:SSS";
    });
    return fmt;
}

@end






